package rcs34.android.AndroidRC.Contacts;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import rcs34.android.AndroidRC.GApp;
import rcs34.android.AndroidRC.R;
import rcs34.android.AndroidRC.Utils;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Event;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Nickname;
import android.provider.ContactsContract.CommonDataKinds.Note;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.text.format.DateFormat;
import android.util.Base64;

public class AContacts {
  public static final int BRIEF = 1;
  public static final int COMPLETE = 2;
  private static final String K_SORTKEY = "sortkey";
  private static final String K_AVAILGROUPS = "availGroups";
  private static final String K_ANAME = "accountName";
  private static final String K_ATYPE = "accountType";
  private static final String K_ID = "_id";
  private static final String K_DELETED = "deleted";
  private static final String K_ID_CONTACT = "idContact";
  private static final String K_ID_RAW = "idRaw";
  private static final String K_ID_DATA = "idData";
  private static final String K_PRIMARY = "primary";
  private static final String K_SPRIMARY = "superPrimary";
  private static final String K_RAW_CONTACTS = "rawContacts";
  private static final String K_NAME = "name";
  private static final String K_EMAILS = "emails";
  private static final String K_PHONES = "phones";
  private static final String K_PHOTO = "photo";
  private static final String K_ORGANIZATIONS = "organizations";
  private static final String K_IMS = "ims";
  private static final String K_NICK = "nick";
  private static final String K_NOTE = "note";
  private static final String K_POSTALS = "postals";
  private static final String K_GROUPS = "groups";
  private static final String K_WEBS = "webs";
  private static final String K_EVENTS = "events";
  private static final String K_RELATIONS = "relations";
  private static final String K_SIPS = "sips";
  private static final String K_RAWS = "raws";
  private static final String[] dataProjection = new String[] {
      ContactsContract.Data.CONTACT_ID, ContactsContract.Data.RAW_CONTACT_ID,
      ContactsContract.Data._ID, ContactsContract.Data.IS_PRIMARY,
      ContactsContract.Data.IS_SUPER_PRIMARY, ContactsContract.Data.MIMETYPE,
      ContactsContract.Data.DATA1, ContactsContract.Data.DATA2,
      ContactsContract.Data.DATA3, ContactsContract.Data.DATA4,
      ContactsContract.Data.DATA5, ContactsContract.Data.DATA6,
      ContactsContract.Data.DATA7, ContactsContract.Data.DATA8,
      ContactsContract.Data.DATA9, ContactsContract.Data.DATA10,
      ContactsContract.Data.DATA11, ContactsContract.Data.DATA12,
      ContactsContract.Data.DATA3, ContactsContract.Data.DATA14,
      ContactsContract.Data.DATA15 };
  private static final String dataClause = ContactsContract.Data.RAW_CONTACT_ID
      + " = ? ";
  private static final String[] rawProjection = new String[] {
      ContactsContract.RawContacts.ACCOUNT_TYPE,
      ContactsContract.RawContacts.ACCOUNT_NAME,
      ContactsContract.RawContacts.CONTACT_ID,
      ContactsContract.RawContacts.DELETED };
  private static final String rawClause = ContactsContract.RawContacts._ID
      + " = ?";
  private static final String[] groupProjection = new String[] {
      ContactsContract.Groups._ID, ContactsContract.Groups.TITLE };
  private static HashMap<String, String> groupsCache = new HashMap<String, String>();
  
  synchronized public static final JSONObject getContactsList() {
    JSONObject unsorted = new JSONObject();
    ArrayList<JSONObject> sorted = new     ArrayList<JSONObject>();
    JSONObject result=new JSONObject();
    Cursor cursor = GApp.contentResolver.query(
        ContactsContract.Data.CONTENT_URI, dataProjection, null, null, null);
    if (cursor != null) {
      if (cursor.moveToFirst()) {
        try {
          prepareGroupsCache();
          while (!cursor.isAfterLast()) {
            String contactId=cursor.getString(0);
            JSONObject contact = Utils.ensureObject(unsorted,
                contactId);
            JSONObject raws = Utils.ensureObject(contact, K_RAWS);
            if(!contact.has(K_ID_CONTACT)){
              contact.put(K_ID_CONTACT, contactId);
            }
            JSONObject rawContact = Utils.ensureObject(raws,
                cursor.getString(1));
            JSONArray emails = Utils.ensureArray(rawContact, K_EMAILS);
            JSONArray phones = Utils.ensureArray(rawContact, K_PHONES);
            JSONArray groups = Utils.ensureArray(rawContact, K_GROUPS);
            String mime = cursor.getString(5);
            if (mime.equals(StructuredName.CONTENT_ITEM_TYPE)) {
              String name = cursor.getString(6);
              rawContact.put(K_NAME, name);
              if (Integer.parseInt(cursor.getString(4)) > 0) {
                contact.put(K_NAME, name);
              }
              if(!contact.has(K_SORTKEY)||(contact.getString(K_SORTKEY).compareToIgnoreCase(name)>0)){
                contact.put(K_SORTKEY, name);
              }
            } else if (mime.equals(Phone.CONTENT_ITEM_TYPE)) {
              phones.put(cursor.getString(6));
            } else if (mime.equals(Email.CONTENT_ITEM_TYPE)) {
              emails.put(cursor.getString(6));
            } else if (mime.equals(Nickname.CONTENT_ITEM_TYPE)) {
              rawContact.put(K_NICK, cursor.getString(6));
            } else if (mime.equals(GroupMembership.CONTENT_ITEM_TYPE)) {
              groups.put(groupsCache.get(cursor.getString(6)));
            }
            cursor.moveToNext();
          }
        } catch (JSONException e) {
          cursor.close();
          e.printStackTrace();
        }
      }
      // System.out.println(result.toString());
      cursor.close();
    }
    //Sort the list of contacts
    try {
      JSONArray unsortedKeys = unsorted.names();
      for (int i = 0; i < unsortedKeys.length(); i++) {
        String uKey = unsortedKeys.getString(i);
        JSONObject uContact = unsorted.getJSONObject(uKey);
        //System.out.println(uContact.length());
        String unsortKey="";
        if (uContact.has(K_SORTKEY)) {
          unsortKey = uContact.getString(K_SORTKEY);
        }
        boolean inserted=false;
        for (int j = 0; j < sorted.size(); j++) {
          JSONObject sContact = sorted.get(j);
          String sortKey="";
          if (sContact.has(K_SORTKEY)) {
          sortKey = sContact.getString(K_SORTKEY);
          }
          if(sortKey.compareToIgnoreCase(unsortKey)>=0){
            sorted.add(j, uContact);
            inserted=true;
            break;
          }
        }
        if(!inserted){
          sorted.add(uContact);
        }
      }
      //Remove auxiliary sortkey and transform ArrayList to JSONArray
      JSONArray sortedJson=new JSONArray();
      for (int i = 0; i < sorted.size(); i++) {
        sorted.get(i).remove(K_SORTKEY);
        sortedJson.put(sorted.get(i));
      }
      //Fold everything into the result object
      result.put("accounts", getAccounts());
      result.put("contacts",sortedJson);
    } catch (JSONException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return result;
  }
  
  public static JSONObject getRawDetails(String _rawId) {
    JSONObject result = new JSONObject();
    Cursor rawCursor = GApp.contentResolver.query(
        ContactsContract.RawContacts.CONTENT_URI, rawProjection, rawClause,
        new String[] { _rawId }, null);
    Cursor dataCursor = GApp.contentResolver.query(
        ContactsContract.Data.CONTENT_URI, dataProjection, dataClause,
        new String[] { _rawId }, null);
    if (rawCursor != null && dataCursor != null && rawCursor.moveToFirst()
        && dataCursor.moveToFirst()) {
      prepareGroupsCache();
      try {
        JSONArray availableGroups = Utils.ensureArray(result, K_AVAILGROUPS);
        for (String key : groupsCache.keySet()) {
          availableGroups.put(groupsCache.get(key));
        }
        result.put(K_ID_RAW, _rawId);
        result.put(K_ATYPE, rawCursor.getString(0));
        result.put(K_ANAME, rawCursor.getString(1));
        result.put(K_ID_CONTACT, rawCursor.getString(2));
        result.put(K_DELETED, rawCursor.getString(3));
        JSONArray emails = Utils.ensureArray(result, K_EMAILS);
        JSONArray phones = Utils.ensureArray(result, K_PHONES);
        JSONArray groups = Utils.ensureArray(result, K_GROUPS);
        JSONArray ims = Utils.ensureArray(result, K_IMS);
        JSONArray postals = Utils.ensureArray(result, K_POSTALS);
        // JSONArray websites = Utils.ensureArray(result, K_WEBS);
        JSONArray events = Utils.ensureArray(result, K_EVENTS);
        // JSONArray relations = Utils.ensureArray(result, K_RELATIONS);
        JSONArray sips = Utils.ensureArray(result, K_SIPS);
        while (!dataCursor.isAfterLast()) {
          String mime = dataCursor.getString(5);
          if (mime.equals(StructuredName.CONTENT_ITEM_TYPE)) {
            String name = dataCursor.getString(6);
            result.put(K_NAME, name);
            // Primary?
            if (Integer.parseInt(dataCursor.getString(4)) > 0) {
              result.put(K_NAME, name);
            }
          } else if (mime.equals(Phone.CONTENT_ITEM_TYPE)) {
            phones.put(dataCursor.getString(6));
          } else if (mime.equals(Email.CONTENT_ITEM_TYPE)) {
            emails.put(dataCursor.getString(6));
          } else if (mime.equals(Nickname.CONTENT_ITEM_TYPE)) {
            result.put(K_NICK, dataCursor.getString(6));
          } else if (mime.equals(GroupMembership.CONTENT_ITEM_TYPE)) {
            groups.put(groupsCache.get(dataCursor.getString(6)));
          } else if (mime.equals(Photo.CONTENT_ITEM_TYPE)) {
            byte[] data = dataCursor.getBlob(20);
            if (data != null) {
              result.put(K_PHOTO, Base64.encodeToString(data, Base64.DEFAULT));
            }
          } else if (mime.equals(Im.CONTENT_ITEM_TYPE)) {
            ims.put(dataCursor.getString(6));
          } else if (mime.equals(Note.CONTENT_ITEM_TYPE)) {
            result.put(K_NOTE, dataCursor.getString(6));
          } else if (mime.equals(StructuredPostal.CONTENT_ITEM_TYPE)) {
            postals.put(dataCursor.getString(6));
            // } else if (mime.equals(Website.CONTENT_ITEM_TYPE)) {
            // websites.put(dataCursor.getString(6));
          } else if (mime.equals(Event.CONTENT_ITEM_TYPE)) {
            events.put(dataCursor.getString(6));
            // } else if (mime.equals(Relation.CONTENT_ITEM_TYPE)) {
            // relations.put(dataCursor.getString(6));
          } else if (mime.equals(SipAddress.CONTENT_ITEM_TYPE)) {
            sips.put(dataCursor.getString(6));
          }
          dataCursor.moveToNext();
        }
      } catch (JSONException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      rawCursor.close();
      dataCursor.close();
    }
    return result;
  }
  
  private static JSONArray getAccounts() {
    AccountManager am = AccountManager.get(GApp.context);
    Account[] accounts = am.getAccounts();
    JSONArray result = new JSONArray();
    try {
      for (int i = 0; i < accounts.length; i++) {
        Account account = accounts[i];
        JSONObject jAccount = new JSONObject();
        jAccount.put("type", account.type);
        jAccount.put("name", account.name);
        result.put(jAccount);
      }
    } catch (JSONException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return result;
  }
  
  private static void prepareGroupsCache() {
    groupsCache.clear();
    Cursor cursor = GApp.contentResolver.query(
        ContactsContract.Groups.CONTENT_URI, groupProjection, null, null, null);
    if (cursor != null && cursor.moveToFirst()) {
      while (!cursor.isAfterLast()) {
        groupsCache.put(cursor.getString(0), cursor.getString(1));
        cursor.moveToNext();
      }
    }
    cursor.close();
  }
  
  public static int deleteRawContact(String _rawId) {
    int result = GApp.contentResolver.delete(
        ContactsContract.RawContacts.CONTENT_URI, rawClause,
        new String[] { _rawId });
    return result;
  }
  
  public static int updateRawContact(JSONObject _spec) {
    RawUpdateBatch batch = new RawUpdateBatch(_spec);
    try {
      return GApp.contentResolver.applyBatch(ContactsContract.AUTHORITY,
          batch.getBatch()).length;
    } catch (RemoteException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (OperationApplicationException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return 0;
  }
  
  public static JSONObject createRawContact(JSONObject _spec) {
    JSONObject result = new JSONObject();
    try {
      String name = _spec.getString("name");
      String type = _spec.getString("type");
      if (name == null || type == null) {
        return result;
      }
      ContentValues values = new ContentValues();
      values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, type);
      values.put(ContactsContract.RawContacts.ACCOUNT_NAME, name);
      Uri uri = GApp.contentResolver.insert(
          ContactsContract.RawContacts.CONTENT_URI, values);
      String rawId = uri.getLastPathSegment();
      values.clear();
      ArrayList<ContentProviderOperation> batch = new ArrayList<ContentProviderOperation>();
      ContentProviderOperation op = ContentProviderOperation
          .newInsert(ContactsContract.Data.CONTENT_URI)
          .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawId)
          .withValue(ContactsContract.Data.MIMETYPE,
              StructuredName.CONTENT_ITEM_TYPE)
          .withValue(ContactsContract.Data.DATA1,
              "_" + GApp.resources.getString(R.string.nnewContact)).build();
      batch.add(op);
      String date = DateFormat.getDateFormat(GApp.context).format(new Date());
      op = ContentProviderOperation
          .newInsert(ContactsContract.Data.CONTENT_URI)
          .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawId)
          .withValue(ContactsContract.Data.MIMETYPE, Note.CONTENT_ITEM_TYPE)
          .withValue(ContactsContract.Data.DATA1,
              GApp.resources.getString(R.string.iinserted) + ": " + date)
          .build();
      batch.add(op);
      GApp.contentResolver.applyBatch(ContactsContract.AUTHORITY, batch);
      result.put("rawId", rawId);
    } catch (JSONException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (RemoteException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (OperationApplicationException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return result;
  }
}
